package astudy.lucene;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.NumericField;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.Term;
import org.apache.lucene.index.TermDocs;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.search.DocIdSet;
import org.apache.lucene.search.Filter;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.NumericRangeQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopScoreDocCollector;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.OpenBitSet;
import org.apache.lucene.util.Version;

public class IndexAndSearch {

	private static Log logger = LogFactory.getLog(IndexAndSearch.class);
	
	public static void main(String[] args) throws Exception {

//		File resourceFolder = new File("D:\\study\\eBooks");
		File resourceFolder = new File("D:\\tools");
		String indexSubFolder = resourceFolder.getCanonicalPath().replaceAll("\\:|\\\\", "_");
		File indexFolder = new File("D:\\temp\\.lucene_index\\" + indexSubFolder);
		index(indexFolder, resourceFolder);
		search(indexFolder);
	}
	
	private static void index(File indexFolder, File resourceFolder) 
	throws IOException {
		
		Analyzer luceneAnalyzer = new StandardAnalyzer(Version.LUCENE_29);
		final IndexWriter indexWriter = new IndexWriter(FSDirectory.open(indexFolder), 
				luceneAnalyzer, true, IndexWriter.MaxFieldLength.LIMITED);
		long startTime = System.currentTimeMillis();

		List<File> files = findFiles(resourceFolder, new FileHandler() {

			public void handle(int i, File file) throws IOException {
				
				logger.info(String.format("Indexing %8d: %s", i+1, file.getCanonicalPath()));
				String name = file.getName();
				int index = name.lastIndexOf('.');
				String type = "-";
				if (index > -1) {
					type = name.substring(index+1);
				}
				Document document = new Document();
				document.add(new Field("id", String.valueOf(i+1), Field.Store.YES, Field.Index.NOT_ANALYZED));
				document.add(new Field("path", file.getCanonicalPath(), Field.Store.YES, Field.Index.NOT_ANALYZED));
				document.add(new Field("name", name, Field.Store.NO, Field.Index.ANALYZED));
				document.add(new Field("type", type, Field.Store.NO, Field.Index.NOT_ANALYZED));
				document.add(new NumericField("size", Field.Store.YES, true).setLongValue(file.length()));
				indexWriter.addDocument(document);
			}
			
		});
		//optimize()方法是对索引进行优化  
		indexWriter.optimize();
		indexWriter.close();

		logger.info(String.format("%d files indexed, Time Cost: %d ms", files.size(), System.currentTimeMillis() - startTime));
	}
	
	private interface FileHandler {
		
		public void handle(int i, File f) throws IOException;
	}
	
	private static List<File> findFiles(File folder, FileHandler hanlder) throws IOException {

		if (!folder.exists() || folder.isFile()) {
			return new ArrayList<File>(0);
		}
		
		List<File> files = new ArrayList<File>();
		
		Queue<File> queue = new LinkedList<File>();
		queue.add(folder);
		
		while (!queue.isEmpty()) {
			File f = queue.poll();
			if (f.isFile()) {
				hanlder.handle(files.size(), f);
				files.add(f);
			} else {
				queue.addAll(Arrays.asList(f.listFiles()));
			}
		}
		
		return files;
	}
	
	private static void search(File indexFolder) 
	throws CorruptIndexException, IOException, ParseException {
		
		IndexSearcher searcher = new IndexSearcher(FSDirectory.open(indexFolder), true);  
//        QueryParser parser = new QueryParser(Version.LUCENE_29, "name", new StandardAnalyzer(Version.LUCENE_29));  
//        Query query = parser.parse("seo");
		
		long startTime = System.currentTimeMillis();
		Query query = NumericRangeQuery.newLongRange("size", 1L, 80000L, true, true);
		Filter filter = new Filter() {

			private static final long serialVersionUID = -4135232274513903194L;

			public DocIdSet getDocIdSet(IndexReader reader) throws IOException {
				OpenBitSet bitSet = new OpenBitSet(reader.maxDoc());
				long startTime = System.currentTimeMillis();
				for (int i = 0; i < 100000; i ++) {
					TermDocs termDocs = reader.termDocs(new Term("id", String.valueOf(i)));
					while (termDocs.next()) {
						bitSet.set(termDocs.doc());
					}
				}
		        logger.info(String.format("Filter Time: %d ms", System.currentTimeMillis() - startTime));
				return bitSet;
			}
		};

        TopScoreDocCollector c = TopScoreDocCollector.create(100, true);
        searcher.search(query, filter, c);

        ScoreDoc[] scoreDocs = c.topDocs(0, c.getTotalHits()).scoreDocs;  
 
//        for (ScoreDoc scoreDoc : scoreDocs) {
//        	Document doc = searcher.doc(scoreDoc.doc);
//        	System.out.println(String.format("%8d, %4.4f:%s: %s (%s bytes)", 
//        					scoreDoc.doc, scoreDoc.score, 
//        					doc.get("id"), doc.get("path"), doc.get("size")));
//        }
        logger.info(String.format("Number of Results: %d, Time: %d ms", 
        				c.getTotalHits(), System.currentTimeMillis() - startTime));
	}
}
